<!-- PreviewIframe.vue -->
<template>
  <!-- 预览容器 -->
  <div v-if="showPreview" class="preview-wrapper">
    <button class="preview-close-btn" @click="closePreview">返回</button>

    <!-- 加载状态 -->
    <div v-if="loading" class="loading-layer">
      <div class="loading-spinner"></div>
      <p>加载中...</p>
    </div>

    <!-- 主iframe -->
    <iframe
      ref="iframeRef"
      :src="targetUrl"
      class="preview-iframe"
      @load="handleIframeLoad"
      @error="handleIframeError"
    ></iframe>
  </div>
</template>

<script setup>
import { ref, watch, onMounted, onUnmounted } from 'vue'

const props = defineProps({
  // 外部URL
  url: {
    type: String,
    required: true
  }
})

const emit = defineEmits(['preview-open', 'preview-close'])

// 响应式状态
const showPreview = ref(false)
const loading = ref(true)
const error = ref(false)
const iframeRef = ref(null)
const targetUrl = ref('')

// 历史记录处理
let historyState = null

// 打开预览
const openPreview = () => {
  if (!props.url) return
  showPreview.value = true
  targetUrl.value = props.url
  historyState = history.state

  // 添加虚拟历史记录
  history.pushState({ isPreview: true }, '')
  emit('preview-open')
}

// 关闭预览
const closePreview = () => {
  showPreview.value = false
  targetUrl.value = ''

  // 恢复历史记录
  if (history.state?.isPreview) {
    history.back()
  }
  emit('preview-close')
}

// 处理iframe加载
const handleIframeLoad = () => {
  loading.value = false
  try {
    // 跨域检测
    const doc =
      iframeRef.value.contentDocument || iframeRef.value.contentWindow.document
    if (!doc.body.innerHTML) {
      throw new Error('Cross origin restriction')
    }
  } catch (e) {
    handlePreviewError()
  }
}

// 处理加载错误
const handleIframeError = () => {
  handlePreviewError()
}

// 错误降级处理
const handlePreviewError = () => {
  error.value = true
  closePreview()
  window.open(props.url, '_blank')
}

// 浏览器返回键处理
const handlePopState = (event) => {
  if (event.state?.isPreview) {
    closePreview()
  }
}

// 生命周期
onMounted(() => {
  window.addEventListener('popstate', handlePopState)
})

onUnmounted(() => {
  window.removeEventListener('popstate', handlePopState)
})

// 暴露方法
defineExpose({
  openPreview,
  closePreview
})
</script>

<style scoped>
.preview-wrapper {
  touch-action: pan-y;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: white;
  z-index: 9999;
}

.preview-iframe {
  width: 100%;
  height: 100%;
  border: none;
}

.preview-close-btn {
  position: fixed;
  top: 12px;
  right: 12px;
  z-index: 10000;
  padding: 8px 16px;
  background: rgba(0, 0, 0, 0.5);
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.loading-layer {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
}

.loading-spinner {
  display: inline-block;
  width: 40px;
  height: 40px;
  border: 3px solid #f3f3f3;
  border-radius: 50%;
  border-top-color: #3498db;
  animation: spin 1s ease-in-out infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
</style>
